//=============================================================================
//                                                                            
//                               OpenMesh                                     
//      Copyright (C) 2001-2005 by Computer Graphics Group, RWTH Aachen       
//                           www.openmesh.org                                 
//                                                                            
//-----------------------------------------------------------------------------
//                                                                            
//                                License                                     
//                                                                            
//   This library is free software; you can redistribute it and/or modify it 
//   under the terms of the GNU Library General Public License as published  
//   by the Free Software Foundation, version 2.                             
//                                                                             
//   This library is distributed in the hope that it will be useful, but       
//   WITHOUT ANY WARRANTY; without even the implied warranty of                
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU         
//   Library General Public License for more details.                          
//                                                                            
//   You should have received a copy of the GNU Library General Public         
//   License along with this library; if not, write to the Free Software       
//   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                 
//                                                                            
//-----------------------------------------------------------------------------
//                                                                            
//   $Revision: 1.4 $
//   $Date: 2005-12-21 13:48:38 $
//                                                                            
//=============================================================================


//=============================================================================
//
//  Helper Functions for binary reading / writing
//
//=============================================================================

#ifndef OPENMESH_SR_RBO_HH
#define OPENMESH_SR_RBO_HH


//== INCLUDES =================================================================

#include <Core/System/config.hh>
// -------------------- STL
#if defined(OM_CC_MIPS)
#  include <stdio.h> // size_t
#else
#  include <cstdio>  // size_t
#endif
#include <algorithm>
#include <typeinfo>
// -------------------- OpenMesh
#include <Core/System/omstream.hh>
#include <Core/IO/SR_types.hh>

//== NAMESPACES ===============================================================

namespace OpenMesh {
	namespace IO {


		//=============================================================================


		/** \name Handling binary input/output.
		These functions take care of swapping bytes to get the right Endian.
		*/
		//@{


		//-----------------------------------------------------------------------------

		template < size_t N > inline 
			void _reverse_byte_order_N(uint8_t* _val)
		{
			//  compile_time_error__only_for_fundamental_types(_val);
		}


		template <> inline
			void _reverse_byte_order_N<1>(uint8_t* _val) { _val; };


		template <> inline
			void _reverse_byte_order_N<2>(uint8_t* _val)
		{
			_val[0] ^= _val[1]; _val[1] ^= _val[0]; _val[0] ^= _val[1];
		}


		template <> inline
			void _reverse_byte_order_N<4>(uint8_t* _val)
		{
			_val[0] ^= _val[3]; _val[3] ^= _val[0]; _val[0] ^= _val[3]; // 0 <-> 3
			_val[1] ^= _val[2]; _val[2] ^= _val[1]; _val[1] ^= _val[2]; // 1 <-> 2
		}


		template <> inline
			void _reverse_byte_order_N<8>(uint8_t* _val)
		{
			_val[0] ^= _val[7]; _val[7] ^= _val[0]; _val[0] ^= _val[7]; // 0 <-> 7
			_val[1] ^= _val[6]; _val[6] ^= _val[1]; _val[1] ^= _val[6]; // 1 <-> 6
			_val[2] ^= _val[5]; _val[5] ^= _val[2]; _val[2] ^= _val[5]; // 2 <-> 5
			_val[3] ^= _val[4]; _val[4] ^= _val[3]; _val[3] ^= _val[4]; // 3 <-> 4
		}


		template <> inline
			void _reverse_byte_order_N<12>(uint8_t* _val)
		{
			_val[0] ^= _val[11]; _val[11] ^= _val[0]; _val[0] ^= _val[11]; // 0 <-> 11
			_val[1] ^= _val[10]; _val[10] ^= _val[1]; _val[1] ^= _val[10]; // 1 <-> 10
			_val[2] ^= _val[ 9]; _val[ 9] ^= _val[2]; _val[2] ^= _val[ 9]; // 2 <->  9
			_val[3] ^= _val[ 8]; _val[ 8] ^= _val[3]; _val[3] ^= _val[ 8]; // 3 <->  8
			_val[4] ^= _val[ 7]; _val[ 7] ^= _val[4]; _val[4] ^= _val[ 7]; // 4 <->  7
			_val[5] ^= _val[ 6]; _val[ 6] ^= _val[5]; _val[5] ^= _val[ 6]; // 5 <->  6
		}


		template <> inline
			void _reverse_byte_order_N<16>(uint8_t* _val)
		{
			_reverse_byte_order_N<8>(_val);
			_reverse_byte_order_N<8>(_val+8);
			std::swap(*(uint64_t*)_val, *(((uint64_t*)_val)+1));
		}


		//-----------------------------------------------------------------------------
		// wrapper for byte reordering

		// reverting pointers makes no sense, hence forbid it.
		template <typename T> inline T* reverse_byte_order(T* t)
		{
			// Should never reach this point. If so, then some operator were not
			// overloaded. Especially check for IO::binary<> specialization on
			// custom data types.
			//  compile_time_error__cannot_do_that(a);
			return t;
		}

		//#if defined(OM_CC_MSVC)
		inline void compile_time_error__no_fundamental_type()
		{
			// we should never reach this point
			assert(false);
		}
		//#endif

		// default action for byte reversal: cause an error to avoid
		// surprising behaviour!
		template <typename T> T& reverse_byte_order(  T& _t )
		{
			omerr() << "Not defined for type " << typeid(T).name() << std::endl;
			compile_time_error__no_fundamental_type();
			return _t;
		}

		template <> inline bool&  reverse_byte_order(bool & _t) { return _t; }
		template <> inline char&  reverse_byte_order(char & _t) { return _t; }
#if defined(OM_CC_GCC)
		template <> inline signed char&  reverse_byte_order(signed char & _t) { return _t; }
#endif
		template <> inline uchar& reverse_byte_order(uchar& _t) { return _t; }

		// Instead do specializations for the necessary types
#define REVERSE_FUNDAMENTAL_TYPE( T ) \
	template <> inline T& reverse_byte_order( T&  _t ) {\
	_reverse_byte_order_N<sizeof(T)>( reinterpret_cast<uint8_t*>(&_t) ); \
	return _t; \
		}

		// REVERSE_FUNDAMENTAL_TYPE(bool);
		// REVERSE_FUNDAMENTAL_TYPE(char);
		// REVERSE_FUNDAMENTAL_TYPE(uchar);
		REVERSE_FUNDAMENTAL_TYPE(int16_t);
		REVERSE_FUNDAMENTAL_TYPE(uint16_t);
		REVERSE_FUNDAMENTAL_TYPE(int);
		REVERSE_FUNDAMENTAL_TYPE(uint);
		REVERSE_FUNDAMENTAL_TYPE(int32_t);
		REVERSE_FUNDAMENTAL_TYPE(uint32_t);
		REVERSE_FUNDAMENTAL_TYPE(int64_t);
		REVERSE_FUNDAMENTAL_TYPE(uint64_t);
		REVERSE_FUNDAMENTAL_TYPE(float);
		REVERSE_FUNDAMENTAL_TYPE(double);
		REVERSE_FUNDAMENTAL_TYPE(long double);

#undef REVERSE_FUNDAMENTAL_TYPE

#if 0

#define REVERSE_VECTORT_TYPE( T ) \
	template <> inline T& reverse_byte_order(T& _v) {\
	for (size_t i; i< T::size_; ++i) \
	_reverse_byte_order_N< sizeof(T::value_type) >( reinterpret_cast<uint8_t*>(&_v[i])); \
	return _v; \
		}

#define REVERSE_VECTORT_TYPES( N )  \
	REVERSE_VECTORT_TYPE( Vec##N##c )  \
	REVERSE_VECTORT_TYPE( Vec##N##uc ) \
	REVERSE_VECTORT_TYPE( Vec##N##s )  \
	REVERSE_VECTORT_TYPE( Vec##N##us ) \
	REVERSE_VECTORT_TYPE( Vec##N##i )  \
	REVERSE_VECTORT_TYPE( Vec##N##ui ) \
	REVERSE_VECTORT_TYPE( Vec##N##f )  \
	REVERSE_VECTORT_TYPE( Vec##N##d )  \

		REVERSE_VECTORT_TYPES(1)
			REVERSE_VECTORT_TYPES(2)
			REVERSE_VECTORT_TYPES(3)
			REVERSE_VECTORT_TYPES(4)
			REVERSE_VECTORT_TYPES(6)

#undef REVERSE_VECTORT_TYPES
#undef REVERSE_VECTORT_TYPE

#endif 

			template <typename T> inline
			T reverse_byte_order(const T& a)
		{
			compile_timer_error__const_means_const(a);
			return a;
		}


		//@}


		//=============================================================================
	} // namespace IO
} // namespace OpenMesh
//=============================================================================
#endif // OPENMESH_SR_RBO_HH defined
//=============================================================================

